import { type FormProps as AntFormProps, Form, Row } from "antd";
import { cloneDeep, pick } from "lodash-es";
import type { FormActionType, FormProps, FormSchemaInner as FormSchema, AdvanceState } from "./types/form";
import { BasicPropsType, BasicPropsDefault } from "./props";
import FormItem from "./components/FormItem";
import FormAction from "./components/FormAction";
import { dateItemType } from "./helper";
import { useFormValues } from "./hooks/useFormValues";
import useAdvanced from "./hooks/useAdvanced";
import { useFormEvents } from "./hooks/useFormEvents";
import { TableActionType } from "@/components/Table";
import moment from "moment";
import { useEffect, useMemo, useRef, useState } from "react";
import { deepMerge } from "@/utils";
import "./style/BasicForm.less";
import { childrenToMap } from "@/utils/helper/tsxHelper";

interface ComponentProps extends ComponentNode {
	register: Function;
	// to realize Vue "emit" function.
	advancedChange?: Function;
	submit?: Function;
	reset?: Function;
}

const BasicForm = (props: BasicPropsType & ComponentProps) => {
	// fix React18 "defaultProps" problem
	props = Object.assign({}, { ...BasicPropsDefault, ...props });

	const slots = childrenToMap(props.children);
	const prefixCls = "basic-form";
	const [formModel, setFormModel] = useState<Recordable>({});
	const [advanceState, setAdvanceState] = useState<AdvanceState>({
		isAdvanced: true,
		hideAdvanceBtn: false,
		isLoad: false,
		actionSpan: 6
	});
	const isInitedDefaultRef = useRef(false);
	const propsRef = useRef<Partial<FormProps>>();
	const [schemaRef, setSchemaRef] = useState<FormSchema[] | null>(null);
	const [formElRef] = Form.useForm();

	// Get the basic configuration of the form
	const getProps = useMemo(() => {
		return { ...props, ...propsRef.current } as FormProps;
	}, [propsRef.current]);

	const getFormClass = useMemo(() => {
		let classList = [prefixCls, props.className];
		if (getProps.compact) classList.push(`${prefixCls}--compact`);
		return classList.join(" ");
	}, [props.className, getProps.compact]);

	// Get uniform row style and Row configuration for the entire form
	const getRow = useMemo(() => {
		const { baseRowStyle = {}, rowProps } = getProps;
		return {
			style: baseRowStyle,
			...rowProps
		};
	}, [getProps.baseRowStyle, getProps.rowProps]);

	const getBindValue = useMemo(() => {
		let propsData: any = {
			...props,
			...getProps,
			formModel
		};
		// React Dom not support these attributes.
		propsData = pick(propsData, [
			"colon",
			"disabled",
			"component",
			"fields",
			"form",
			"initialValues",
			"labelAlign",
			"labelWrap",
			"labelCol",
			"layout",
			"name",
			"preserve",
			"requiredMark",
			"scrollToFirstError",
			"size"
		]);
		return propsData as AntFormProps;
	}, [getProps]);

	const getSchema = useMemo((): FormSchema[] => {
		const schemas: FormSchema[] = schemaRef || (getProps.schemas as any);
		if (!schemas || !schemas.length) return [];
		for (const schema of schemas) {
			const { defaultValue, component, componentProps, isHandleDateDefaultValue = true } = schema;

			const valueFormat = componentProps ? componentProps?.valueFormat : null;
			// handle date type
			if (isHandleDateDefaultValue && defaultValue && component && dateItemType.includes(component)) {
				if (!Array.isArray(defaultValue)) {
					schema.defaultValue = valueFormat ? moment(defaultValue).format(valueFormat) : moment(defaultValue);
				} else {
					const def: any[] = [];
					defaultValue.forEach(item => {
						def.push(valueFormat ? moment(item).format(valueFormat) : moment(item));
					});
					schema.defaultValue = def;
				}
			}
			// 默认每行3个组件
			// if (!getProps?.baseColProps && !schema?.colProps) schema.colProps = { span: 8 };
		}
		if (getProps.showAdvancedButton) {
			return cloneDeep(schemas.filter(schema => schema.component !== "Divider") as FormSchema[]);
		} else {
			return cloneDeep(schemas as FormSchema[]);
		}
	}, [getProps.schemas, schemaRef, getProps.showAdvancedButton]);

	const { handleToggleAdvanced, fieldsIsAdvancedMap } = useAdvanced({
		advanceState,
		getProps,
		getSchema,
		defaultValueRef: getProps.initialValues,
		setAdvanceState,
		advancedChange: props.advancedChange
	});

	const { handleFormValues, initDefault } = useFormValues({
		getProps,
		defaultValueRef: getProps.initialValues,
		getSchema,
		formElRef
	});

	const { getFieldsValue, setFieldsValue, resetFields, validateFields, handleSubmit, updateSchema, resetSchema } = useFormEvents({
		getProps,
		getSchema,
		formElRef,
		schemaRef: [schemaRef, setSchemaRef],
		handleFormValues,
		setFormModel,
		submit: props.submit,
		reset: props.reset,
		formModel: formModel
	});

	function setProps(formProps: Partial<FormProps>) {
		//  define value by "useState" multiple changes during the same rendering only take effect for the last time.
		//  so use "useRef"
		propsRef.current = deepMerge(propsRef.current || {}, formProps);
	}

	function setFormModelAction(key: string, value: any, schema: FormSchema) {
		setFormModel({
			...formModel,
			[key]: value
		});
		// TODO 优化验证，这里如果是autoLink=false手动关联的情况下才会再次触发此函数
		if (schema && schema.itemProps && !schema.itemProps.autoLink) validateFields([key]).catch(() => {});
	}

	const formActionType: Partial<FormActionType> = {
		...formElRef,
		getFieldsValue,
		setFieldsValue,
		resetFields,
		validateFields,
		handleSubmit,
		updateSchema,
		resetSchema,
		setProps
	};

	const getFormActionBindProps = useMemo(() => {
		return { ...getProps, ...advanceState };
	}, [getProps, advanceState]);

	/** Effect */

	// only register once
	useEffect(() => {
		initDefault();
		props.register && props.register(formActionType);
		return () => {
			// clean
			setAdvanceState({ isAdvanced: true, hideAdvanceBtn: false, isLoad: false, actionSpan: 6 });
			isInitedDefaultRef.current = false;
			propsRef.current = {};
			setSchemaRef(null);
		};
	}, []);

	useEffect(() => {
		resetSchema(getProps.schemas ?? []);
	}, [getProps.schemas]);

	useEffect(() => {
		if (isInitedDefaultRef.current) return;
		if (getSchema?.length) {
			initDefault();
			isInitedDefaultRef.current = true;
		}
	}, [getSchema]);

	const tableAction: TableActionType = props.tableAction;

	return (
		<Form {...getBindValue} className={getFormClass} form={formElRef}>
			<Row {...getRow}>
				{getSchema.map(schema => (
					<FormItem
						key={schema.field}
						isAdvanced={fieldsIsAdvancedMap[schema.field]}
						tableAction={tableAction}
						formActionType={formActionType}
						schema={schema}
						formProps={getProps}
						initialValues={getProps.initialValues}
						slots={slots}
						// 解决动态联动组件重新渲染问题
						formModel={formModel}
						setFormModel={setFormModelAction}
					></FormItem>
				))}
				<FormAction
					{...getFormActionBindProps}
					toggleAdvanced={handleToggleAdvanced}
					formActionType={formActionType}
				></FormAction>
			</Row>
		</Form>
	);
};

export default BasicForm;
